///////////////////////////////////////////////////////////////////////////////
// Loader main program                                                       //
// Created by Hell Hibou (2008)                                              //
// Part of Wii Homebrew Launcher                                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////


#include <gccore.h>
#include <stdio.h>
#include <string.h>
#include <fat.h>
#include <stdlib.h>
#include <wiiuse/wpad.h>
#include "common.h"
#include <sdcard/wiisd_io.h>
#include <ogc/usbstorage.h>

#include "app_dol.hpp"
#include "app_elf.hpp"
#include "processor.h"
#include "asm.h"
#include "AppConfig.h"



static void *xfb = NULL;
static GXRModeObj *rmode = NULL;
int device = 0;

///////////////////////////////////////////////////////////////////////////////
// Don't reload IOS at startup...
///////////////////////////////////////////////////////////////////////////////

int forward(char* path)
{
	T_WHL_Common_Ressources * WHL_Common;
	WHL_Common = GET_GLOBAL_PARAM_ADRESS;
	void (*Reload)() = (void(*)())0x80001800;	
	void (*EntryPoint)();
	C_App * App = NULL;
	u32 Level = 0;
	
	// Initialise the video system
	VIDEO_Init();
	
	// This function initialises the attached controllers
	WPAD_Init();
	
	// Obtain the preferred video mode from the system
	// This will correspond to the settings in the Wii menu
	rmode = VIDEO_GetPreferredMode(NULL);
	if (CONF_GetAspectRatio() == CONF_ASPECT_16_9)
	{
		rmode->viWidth = 678;
		rmode->viXOrigin = (VI_MAX_WIDTH_NTSC - 678)/2;
	}	 

	// Allocate memory for the display in the uncached region
	xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
	
	// Initialise the console, required for printf
	console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ);
	
	// Set up the video registers with the chosen mode
	VIDEO_Configure(rmode);
	
	// Tell the video hardware where our display memory is
	VIDEO_SetNextFramebuffer(xfb);
	
	// Make the display visible
	VIDEO_SetBlack(FALSE);

	// Flush the video register changes to the hardware
	VIDEO_Flush();

	// Wait for Video setup to complete
	VIDEO_WaitVSync();
	
	if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
	printf("\x1b[4;0H");
	printf("Loading %s\n", path);
	
	
	char *usb2;
	char *sd;
	usb2 = strstr (path,"USB");
	sd = strstr (path, "SD");
	if (usb2) {
		device = 3;
		__io_usbstorage.startup();
		if (!fatMountSimple("USB", &__io_usbstorage)) {
			printf("\x1b[5;0H");
			printf("USB device could not be initialized!\n");
			printf("Press A to reboot Wii...\n");
			while (1) {
				WPAD_ScanPads();
				u32 pressed = WPAD_ButtonsDown(0);
				if (pressed & WPAD_BUTTON_A) {
					SYS_ResetSystem(SYS_RESTART,0,0);
				//return -1;
				}
			}
		}
	} else if (sd) {
		device = 1;
		__io_wiisd.startup();
		if (!fatMountSimple("SD", &__io_wiisd)) {
			printf("\x1b[5;0H");
			printf("SD card could not be initialized!\n");
			printf("Press A to reboot Wii...\n");
			while (1) {
				WPAD_ScanPads();
				u32 pressed = WPAD_ButtonsDown(0);
				if (pressed & WPAD_BUTTON_A) {
					SYS_ResetSystem(SYS_RESTART,0,0);
				//return -1;
				}
			}
		}
	}
	
	printf("[                    ]");
	printf("\x1b[5;1H");
	//create a buffer for the elf/dol content
	//void* myBuffer;
	//u8* myBuffer = (u8 *)0x92000000;
	
	//read elf/dol from given path:
	FILE* inputFile;
	inputFile = fopen(path, "rb");

	if(inputFile == NULL) {
		printf("\x1b[5;0H");
		printf("  File %s does not exist on sd!\n", path);
		printf("  Press A to reboot Wii...\n");
		while (1) {
			WPAD_ScanPads();
			u32 pressed = WPAD_ButtonsDown(0);
			if (pressed & WPAD_BUTTON_A) {
				fclose(inputFile);
				SYS_ResetSystem(SYS_RESTART,0,0);
				//return -1;
			}
		}
		
	}
	printf("...");
	
	int pos = ftell(inputFile);
	fseek(inputFile, 0, SEEK_END);
	WHL_Common->DataSize = ftell(inputFile);
	fseek(inputFile, pos, SEEK_SET);
	
	printf("...");

	WHL_Common->Data = (char*) malloc (WHL_Common->DataSize);
	if (WHL_Common->Data == NULL) {
		printf("\x1b[5;0H");
		printf("  error setting buffer\n");
		printf("  Press A to reboot Wii...\n");
		while (1) {
			WPAD_ScanPads();
			u32 pressed = WPAD_ButtonsDown(0);
			if (pressed & WPAD_BUTTON_A) {
				fclose(inputFile);
				SYS_ResetSystem(SYS_RESTART,0,0);
				//return -1;
			}
		}
	}
	size_t result = fread(WHL_Common->Data, 1, WHL_Common->DataSize, inputFile);
	if ((long)result != WHL_Common->DataSize) {
		printf("\x1b[5;0H");
		printf("  error readiong file\n");
		printf("  Press A to reboot Wii...\n");
		while (1) {
			WPAD_ScanPads();
			u32 pressed = WPAD_ButtonsDown(0);
			if (pressed & WPAD_BUTTON_A) {
				fclose(inputFile);
				SYS_ResetSystem(SYS_RESTART,0,0);
				//return -1;
			}
		}
	}
	fflush(inputFile);
	fclose(inputFile);
	if (device == 1) {
		fatUnmount("SD");
	} else if (device == 2) {
		fatUnmount("USB");
	} 
	printf("...");
	
	if (C_App_elf::isElf(WHL_Common->Data))
	{ App = new C_App_elf(); } else { App = new C_App_dol(); }
	App->Load(WHL_Common->Data, path, APP_ACTION_PRELOAD);
	EntryPoint = (void(*)()) App->Load (WHL_Common->Data, NULL, APP_ACTION_RUN);
	Reload = WHL_Common->ep_Reboot;
	SYS_ResetSystem(SYS_SHUTDOWN, 0, 0);
	_CPU_ISR_Disable (Level);
	printf("..");
	EntryPoint();
	free((void*)WHL_Common->DataSize);
	Reload();
	return 0;
}

///////////////////////////////////////////////////////////////////////////////
